﻿// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;
using System.Collections.Generic;
using System.Text;

using FuncLib.DualFunctions.Collections;

namespace FuncLib.DualFunctions.Compilation
{
	internal class SortedHessian : HashedList<HashedTuple<DualVariable, DualVariable>>
	{
		private Dictionary<int, Dictionary<SortedHessian, IndexList>> indexLists = new Dictionary<int, Dictionary<SortedHessian, IndexList>>();
		private Dictionary<int, Dictionary<SortedGradient, IndexList>> indexListsGradient1 = new Dictionary<int, Dictionary<SortedGradient, IndexList>>();
		private Dictionary<int, Dictionary<SortedGradient, IndexList>> indexListsGradient2 = new Dictionary<int, Dictionary<SortedGradient, IndexList>>();

		public SortedHessian()
		{
		}

		public void AddOnce(DualVariable variable1, DualVariable variable2)
		{
			AddOnce(HashedTuple.Create<DualVariable, DualVariable>(variable1, variable2));
		}

		public bool Contains(DualVariable variable1, DualVariable variable2)
		{
			return Contains(HashedTuple.Create<DualVariable, DualVariable>(variable1, variable2));
		}

		public IndexList GetIndexList(int startIndex, SortedHessian hessian, Storage hessianStorage)
		{
			int relativeIndex = hessianStorage.Index - startIndex;

			if (!indexLists.ContainsKey(relativeIndex))
			{
				indexLists.Add(relativeIndex, new Dictionary<SortedHessian, IndexList>());
			}

			IndexList computed;
			if (!indexLists[relativeIndex].TryGetValue(hessian, out computed))
			{
				List<int> offsets = new List<int>(); // no need to use a dynamic array
				foreach (HashedTuple<DualVariable, DualVariable> entry in this)
				{
					offsets.Add(hessian.IndexOf(entry) + relativeIndex);
				}
				computed = new IndexList(offsets.ToArray());

				indexLists[relativeIndex].Add(hessian, computed);
			}

			return computed;
			//return new StorageOffsets(offsets.ToArray());
		}

		public IndexList GetIndexListGradient1(int startIndex, SortedGradient gradient, Storage gradientStorage)
		{
			int relativeIndex = gradientStorage.Index - startIndex;

			if (!indexListsGradient1.ContainsKey(relativeIndex))
			{
				indexListsGradient1.Add(relativeIndex, new Dictionary<SortedGradient, IndexList>());
			}

			IndexList computed;
			if (!indexListsGradient1[relativeIndex].TryGetValue(gradient, out computed))
			{
				List<int> offsets = new List<int>(); // no need to use a dynamic array
				foreach (HashedTuple<DualVariable, DualVariable> entry in this)
				{
					offsets.Add(gradient.IndexOf(entry.Item1) + relativeIndex); // FIXME use SortedDictionary for the lookup
				}
				computed = new IndexList(offsets.ToArray());

				indexListsGradient1[relativeIndex].Add(gradient, computed);
			}

			return computed;
			//return new StorageOffsets(offsets.ToArray());
		}

		public IndexList GetIndexListGradient2(int startIndex, SortedGradient gradient, Storage gradientStorage)
		{
			int relativeIndex = gradientStorage.Index - startIndex;

			if (!indexListsGradient2.ContainsKey(relativeIndex))
			{
				indexListsGradient2.Add(relativeIndex, new Dictionary<SortedGradient, IndexList>());
			}

			IndexList computed;
			if (!indexListsGradient2[relativeIndex].TryGetValue(gradient, out computed))
			{
				List<int> offsets = new List<int>(); // no need to use a dynamic array
				foreach (HashedTuple<DualVariable, DualVariable> entry in this)
				{
					offsets.Add(gradient.IndexOf(entry.Item2) + gradientStorage.Index - startIndex); // FIXME use SortedDictionary for the lookup
				}

				computed = new IndexList(offsets.ToArray());

				indexListsGradient2[relativeIndex].Add(gradient, computed);
			}

			return computed;
			//return new StorageOffsets(offsets.ToArray());
		}
	}
}
